
#pragma once

#include <stddef.h>

#include "core/olisp_cfg.h"

/*
 * defines typename markers which applicable to obj_t::element_type
 * type presence could be checked via == operator
 */
typedef enum
{
    LISP_ELEM_NIL           = 1,
    LISP_ELEM_T             = 2,
    LISP_ELEM_ID            = 3,
    LISP_ELEM_LIST          = 4,
    LISP_ELEM_NUMBER        = 5,
    LISP_ELEM_STRING        = 6,
} elem_type_t;

/*
 * Abstract lisp element
 */

typedef struct
{
    struct obj_vtbl_t * vtbl;
} obj_t;

#define LISPELEM_VTBL( T ) \
    elem_type_t element_type; \
    int (* add_ref)( T * self ); \
    int (* release)( T * self );
	
typedef struct obj_vtbl_t obj_vtbl_t;

struct obj_vtbl_t
{
    LISPELEM_VTBL( obj_t )
};


/*
 * Nil & T
 */

extern obj_t * nil_elem;

extern obj_t * true_elem;


/*
 * Identifier
 */
typedef struct
{
    struct id_obj_vtbl_t * vtbl;
} id_obj_t;

typedef struct id_obj_vtbl_t id_obj_vtbl_t;
struct id_obj_vtbl_t
{
    LISPELEM_VTBL( id_obj_t )

    const char * (* as_str)( id_obj_t * self );
    lisp_hash_t (* as_hash)( id_obj_t * self );
};

id_obj_t * create_identifier( const char * id );

/*
 * List
 */

typedef struct
{
    struct list_obj_vtbl_t * vtbl;
} list_obj_t;

typedef struct list_obj_vtbl_t list_obj_vtbl_t;
struct list_obj_vtbl_t
{
    LISPELEM_VTBL( list_obj_t )

    /*
     * returns the length of the list
     */
    size_t (* length)( list_obj_t * self );

    /*
     * retrieves an element from list on the specified position.
     * returned element is not add_ref'ed by list.
     * if wrong pos is specified - 0 is returned.
     */
    obj_t * (* get_elem)( list_obj_t * self, size_t pos );

    /*
     * inserts element after(or before) element at position pos
     * return 1 if succeeds, 0 - if fail.
     * append/set/remove return the same results as well if they succeeded or not
     */
    int (* insert)( list_obj_t * self, obj_t * elem, size_t pos, int after );
    int (* append)( list_obj_t * self, obj_t * elem );
    int (* set)( list_obj_t * self, size_t pos, obj_t * elem );
    int (* remove)( list_obj_t * self, obj_t * elem );
};


list_obj_t * create_list( size_t initial_length );

/*
 * Number
 */

typedef struct
{
    struct num_obj_vtbl_t * vtbl;
} num_obj_t;

typedef struct num_obj_vtbl_t num_obj_vtbl_t;
struct num_obj_vtbl_t
{
    LISPELEM_VTBL( num_obj_t )
    
    int (* as_int)( num_obj_t * self );
    double (* as_double)( num_obj_t * self );
    int (* is_double)( num_obj_t * self );
};


num_obj_t * create_int_number( int num );

num_obj_t * create_dbl_number( double num );


/*
 * String
 */

typedef struct
{
    struct str_obj_vtbl_t * vtbl;
} str_obj_t;

typedef struct str_obj_vtbl_t str_obj_vtbl_t;
struct str_obj_vtbl_t
{
    LISPELEM_VTBL( str_obj_t )

    const char * (* get_str)( str_obj_t * self );
};


str_obj_t * create_string( const char * str );
